package org.parosproxy.paros.core.proxy.custom;

import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.URIException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.parosproxy.paros.core.proxy.ProxyListener;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpResponseHeader;

/**
 * En esta clase se deberian modificar tanto los REQUESTS como los RESPONSES
 * 
 * En los requests, necesito sacar algunos atributos del header
 * 
 * 1 - Todos los compress gzip y todo eso. Lo que haga que la respuesta no venga
 * en texto plano
 * 
 * 2 - Secure cookies (secure flag). Kill secure bit Set-Cookies. En el caso que
 * este patchendo una conexion HTTS-HTTP
 * 
 * 3 - Los if-modified since, para que no funcione el cache del Browser.
 * 
 * Para los responses, necesito modificar
 * 
 * 1 - Cambiar todos los https a http
 * 
 * @author pable
 * 
 */

public class StripProxyListener implements ProxyListener {

	private static final Pattern urlExpression = Pattern.compile("(https://[\\w\\d:#@%/;$()~_?\\+-=\\\\.&]*)", Pattern.CASE_INSENSITIVE);
	@SuppressWarnings("unused")
	private static final Pattern urlType = Pattern.compile("https://", Pattern.CASE_INSENSITIVE);
	private static final Pattern urlExplicitPort = Pattern.compile("https://([a-zA-Z0-9.]+):[0-9]+/", Pattern.CASE_INSENSITIVE);
	private static final Pattern linkExpression = Pattern.compile("<((a)|(link)|(img)|(script)|(frame)) .*((href)|(src))=\"([\\w\\d:#@%/;$()~_?\\+-=\\\\.&]+)\".*>", Pattern.CASE_INSENSITIVE);
	private static final Pattern cssExpression = Pattern.compile("url\\(([\\w\\d:#@%/;$~_?\\+-=\\\\.&]+)\\)", Pattern.CASE_INSENSITIVE);
	private static final Pattern secureCookie = Pattern.compile("([\\s\\w\\d:\\#@%/;$()~_?\\+-=\\\\\\.&]+); ?Secure", Pattern.CASE_INSENSITIVE | Pattern.COMMENTS | Pattern.MULTILINE | Pattern.DOTALL
			| Pattern.UNICODE_CASE | Pattern.UNIX_LINES);

	private static final Pattern removeSecureCookie = Pattern.compile(";[\\s]*Secure", Pattern.CASE_INSENSITIVE | Pattern.COMMENTS | Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNICODE_CASE
			| Pattern.UNIX_LINES);

	private static Log log = LogFactory.getLog(StripProxyListener.class);

	private SSLLinkBuilder linkBuilder = new SSLLinkBuilder(null);

	private URLMonitor urlMonitor;

	public StripProxyListener() {
		super();
		this.urlMonitor = URLMonitor.getInstance();
	}

	public URLMonitor getUrlMonitor() {
		return urlMonitor;
	}

	public void setUrlMonitor(URLMonitor urlMonitor) {
		this.urlMonitor = urlMonitor;
	}

	@Override
	public void onHttpRequestSend(HttpMessage msg) {
		/*
		 * En el ProxyThrear, el mismo proxy saca los gzip y defalte.
		 * 
		 * if 'accept-encoding' in headers: del headers['accept-encoding']
		 * 
		 * if 'if-modified-since' in headers: del headers['if-modified-since']
		 * 
		 * if 'cache-control' in headers: del headers['cache-control']
		 */
		msg.getRequestHeader().setHeader(HttpHeader.IF_MODIFIED_SINCE, "");

	}

	@Override
	public void onHttpResponseReceive(HttpMessage msg) {

		boolean isSecureLink = urlMonitor.isSecureLink(msg.getRemoteAddress(), msg.getRequestHeader().getURI().toString());

		normalReplace(msg);

		if (isSecureLink) {
			replaceCssLinks(msg.getResponseBody().toString(), msg);
			fixCookies(msg);
			Matcher m = linkExpression.matcher(msg.getResponseBody().toString());
			while (m.find()) {
				try {
					linkBuilder.buildAbsoluteLink(m.group(10), msg.getRequestHeader(), msg.getRemoteAddress(), msg.getRequestHeader().getURI().getPath());
				} catch (URIException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}

	private void fixCookies(HttpMessage msg) {
		Vector vector = msg.getResponseHeader().getHeaders("set-cookie");
		if (vector != null) {
			for (int i = 0; i < vector.size(); i++) {
				Object value = vector.get(i);
				Matcher m = secureCookie.matcher((String) value);
				while (m.find()) {
					Matcher mCoookie = removeSecureCookie.matcher((String) value);
					value = mCoookie.replaceAll("");
					// No se si esto le va a gustar mucho, manosear el vec
					// mientras lo iteras
					vector.set(i, value);
				}
			}
		}
	}

	private void replaceCssLinks(String data, HttpMessage msg) {
		Matcher matcher = cssExpression.matcher(data);
		while (matcher.find()) {
			try {
				linkBuilder.buildAbsoluteLink(matcher.group(1), msg.getRequestHeader(), msg.getRemoteAddress(), msg.getRequestHeader().getURI().getPath());
			} catch (URIException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private void normalReplace(HttpMessage msg) {
		StringBuffer buff = new StringBuffer();

		if (msg.getResponseHeader().getStatusCode() == 301 || msg.getResponseHeader().getStatusCode() == 302) {
			changeLocation(msg);
		}

		Matcher m1 = urlExpression.matcher(msg.getResponseBody().toString());
		while (m1.find()) {
			String url = m1.group();
			log.debug("Found secure reference: " + url);
			url = url.replace("https://", "http://"/* , 1 */);
			url = url.replace("&amp;", "&");
			urlMonitor.addSecureLink(msg.getRemoteAddress(), url);
		}

		Matcher m = urlExplicitPort.matcher(msg.getResponseBody().toString());
		while (m.find()) {
			m.appendReplacement(buff, "http://" + m.group(1) + "/");
		}
		m.appendTail(buff);
		String body = buff.toString().replaceAll("https://", "http://");
		msg.getResponseBody().setBody(body);
	}

	private void changeLocation(HttpMessage msg) {
		String url = msg.getResponseHeader().getHeader(HttpResponseHeader.LOCATION);
		String message = "Changing redirect form \"" + url + "\"";
		url = url.replace("https://", "http://"/* , 1 */);
		url = url.replace("&amp;", "&");
		message += " to \"" + url;
		log.info(message);
		urlMonitor.addSecureLink(msg.getRemoteAddress(), url);
		msg.getResponseHeader().setHeader(HttpResponseHeader.LOCATION, url);
	}

}
